﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ChainAnalises.Classes.DivizionToAccords;
using ChainAnalises.Classes.DivizionToAccords.Criteria;
using ChainAnalises.Classes.IntervalAnalysis;
using Segmentator.Interfaces;

namespace Segmentator.Dividers
{
    /// <summary>
    /// Алгоритм разбиения знаковой последовательности, в котором используется частичный критерий Орлова
    /// (точность совпадения расчетно-теоретического и фактического размеров словаря склеек).
    /// </summary>
    class OrlovPartialCriterion:Algorithm
    {
        public static readonly string NAME = "OrlovPartialCriterion";
        /// <summary>
        /// Загружает данные для обработки 
        /// </summary>
        /// <param name="data"></param>
        public override void LoadData(IDataLoader data)
        {
            Data.ThresholdLeft = data.GetThresholdLeft();
            Data.ThresholdRight = data.GetThresholdRight();
            Data.ThresholdStep = data.GetThresholdStep();
            Data.ChainName = data.GetChainName();
            Data.IsSavingResults = data.IsSavingResults();
            Data.Sequence = data.GetSequence();
            Data.Precision = data.GetPrecision();
            Data.LengthStartWindow = data.GetLengthStartWindow();
            Data.AmountStartAlgorithm = data.GetAmountStartAlgorithm();
            Data.MinAlphabetsDistortion = data.GetMinAlphabetsDistortion();
            Data.BalanceFactor = data.GetBalanceFactor();
 
        }

        /// <summary>
        /// Выгружает результаты сегментации
        /// </summary>
        public override CalculatorResults UpLoadData()
        {
            return new CalculatorResults(new Chain(Data.Sequence), ConvolutedChain, Alphabet, Threshold);
        }

        /// <summary>
        /// Сегментирует выбранную знаковую последовательность с учетом принятых параметров
        /// Сегментация будет продолжаться до тех пор, пока разница между теоритическим и 
        /// практическим объемами расхождение будет больше заданно величины
        /// </summary>
        public override void Slot()
        {
            OrlovDivider OrlovDivider = null;
            ThresholdVariator thresholdVariator = new DichotomyThreshold(Data.ThresholdLeft, Data.ThresholdRight);
            double AlphabetDistortion = 100, tVolume = 0, pVolume = 0;

            while ((Data.MinAlphabetsDistortion <= AlphabetDistortion) && (thresholdVariator.GetDistortion() >= Data.Precision) )
            {
                StdCalculator._collector = new EntryCollector();
                OrlovDivider = new OrlovDivider(new Chain(Data.Sequence), Data.BalanceFactor / 100.0, Data.LengthStartWindow, Algorithm._linkUp, Method.Convoluted); // отдать настройки. Создали сегментатор который выдаст просегментированную цепь
                OrlovDivider.DivideChain(thresholdVariator.next(AlphabetDistortion));
                Alphabet = OrlovDivider.GetAlphabet(); // Взял алфавит после сегментации
                
                ConvolutedChain = OrlovDivider.GetConvolutedChain(); //Взял просегментированную цепь
                tVolume = GetTheoryVolume(Alphabet, ConvolutedChain);
                pVolume = Alphabet.power;
                AlphabetDistortion =  tVolume - pVolume; // расхождение алфавитов
                
            }

            Threshold = thresholdVariator.GetCurrent();
        }

        /// <summary>
        /// Теоретический объем алфавита
        /// </summary>
        /// <param name="alphabet"></param>
        /// <param name="chain"></param>
        /// <returns></returns>
        private double GetTheoryVolume(AlphabetChain alphabet, Chain chain)
        {
            double one = 0;
            double f = 0;
            foreach (Chain element in alphabet)
            {
                one = one + Calculator.frequency(chain, element);
                if (Calculator.frequency(chain, element) > f) f = Calculator.frequency(chain, element);
            }
            double z = chain.Length;
            double k = 1/(Math.Log(f * z));
            double b = k/f - 1;
            double v = k*z-b;

            return v;
        }
        }
}
